import java.awt.Point;
import java.util.ArrayList;
import java.util.Collections;


public class Solver implements Runnable {
	
	public boolean doSolve = false;
	private int activeWaitTime = 200;
	private int inactiveWaitTime = 1000;
	
	private ArrayList<Move> moves;
	public Gem[][] gems;
	
	private Move moveMade;

    public Solver () {
        moves = new ArrayList<Move>(10);
        gems = new Gem[Application.GetImageManager().gridCountX][Application.GetImageManager().gridCountY];
        for(int x=0; x<Application.GetImageManager().gridCountX; x++) {
        	for(int y=0; y<Application.GetImageManager().gridCountY; y++) {
        		gems[x][y] = new Gem(x, y, Gem.GemType.none);
        	}
        }
    }
    
	public void run(){
		while(true) {
			if(doSolve) {
				Solve();
				try {
					Thread.sleep(activeWaitTime);
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
			} else {
				try {
					Thread.sleep(inactiveWaitTime);
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
			}
		}
	}
	
	private void Solve() {
		ImageManager im = Application.GetImageManager();
		
		ResetGems();
		im.CaptureGameGrid();
		im.RunGemDetectionFilters(gems);
		
		moves.clear();
		FindAllMoves();
		if(moves.size() == 0)
			return;
		Collections.sort(moves);
		Move moveToMake = moves.get(moves.size() - 1);
		if(moveMade != null) {
			for(int i=moves.size() - 2; i > -1; i--) {
				Move preferredMove = moves.get(i);
				//Check if preferredMove doesn't intersect with moveMade
				if(moveMade.EffectAreasContainPoint(new Point(preferredMove.gem1.x, preferredMove.gem1.y)) ||
					moveMade.EffectAreasContainPoint(new Point(preferredMove.gem2.x, preferredMove.gem2.y))) {
					continue;
				} else {
					moveToMake = preferredMove;
					break;
				}
			}
		}
		Point startPoint = GetGemScreenPosition(moveToMake.gem1);
		Point endPoint = GetGemScreenPosition(moveToMake.gem2);
		Application.GetMouseRobot().MakeMove(startPoint, endPoint);
	}
	
	public void ResetGems() {
		ImageManager im = Application.GetImageManager();
		for(int x=0; x<im.gridCountX; x++) {
			for(int y=0; y<im.gridCountY; y++) {
				gems[x][y].type = Gem.GemType.none;
			}
		}
	}
	
	private Point GetGemScreenPosition(Gem gem) {
		return new Point((int) (Application.GetImageManager().gameGridRect.x + (gem.x + 0.5f) * Application.GetImageManager().gridSizeX),
						(int) (Application.GetImageManager().gameGridRect.y + (gem.y + 0.5f) * Application.GetImageManager().gridSizeY));
	}
	
	private void FindAllMoves() {
		for(int x=0; x<Application.GetImageManager().gridCountX; x++) {
			for(int y=0; y<Application.GetImageManager().gridCountY; y++) {
				Move newMoveToMake = null;
				//Move right
				if(x < Application.GetImageManager().gridCountX - 1) {
					newMoveToMake = new Move (gems[x][y], gems[x+1][y]);
					FindAdjacency(newMoveToMake, CreateGemArray(x-2, y, true, 6, gems[x][y], gems[x+1][y]));
					FindAdjacency(newMoveToMake, CreateGemArray(x, y-2, false, 5, gems[x][y], gems[x+1][y]));
					FindAdjacency(newMoveToMake, CreateGemArray(x+1, y-2, false, 5, gems[x][y], gems[x+1][y]));
					if(newMoveToMake != null && newMoveToMake.points > 0)
						moves.add(newMoveToMake);
				}
				//Move down
				if(y < Application.GetImageManager().gridCountY - 1) {
					newMoveToMake = new Move (gems[x][y], gems[x][y+1]);
					FindAdjacency(newMoveToMake, CreateGemArray(x-2, y, true, 5, gems[x][y], gems[x][y+1]));
					FindAdjacency(newMoveToMake, CreateGemArray(x-2, y+1, true, 5, gems[x][y], gems[x][y+1]));
					FindAdjacency(newMoveToMake, CreateGemArray(x, y-2, false, 6, gems[x][y], gems[x][y+1]));
					if(newMoveToMake != null && newMoveToMake.points > 0)
						moves.add(newMoveToMake);
				}
			}
		}
	}
	
	private Gem[] CreateGemArray(int x, int y, boolean horiz, int length, Gem gem1, Gem gem2) {
		//clip x and y against board size
		if(x < 0) {
			if(horiz)
				length += x;
			x = 0;
		}
		if(y < 0) {
			if(!horiz)
				length += y;
			y = 0;
		}
		//clip length against board size
		if(horiz) {
			if(x + length > Application.GetImageManager().gridCountX)
				length = Application.GetImageManager().gridCountX - x;
		} else {
			if(y + length > Application.GetImageManager().gridCountY)
				length = Application.GetImageManager().gridCountY - y;
		}

		if(length > 0) {
			Gem[] newgems = new Gem[length];
			for	(int i=0; i<length; i++) {
				if(gems[x][y].equals(gem1))
					newgems[i] = new Gem(gem1.x, gem1.y, gem2.type);
				else if(gems[x][y].equals(gem2))
					newgems[i] = new Gem(gem2.x, gem2.y, gem1.type);
				else
					newgems[i] = gems[x][y];
				if(horiz)
					x++;
				else
					y++;	
			}
			return newgems;
		} else {
			return null;
		}
	}
	
	private void FindAdjacency(Move move, Gem[] newGems) {
		if(newGems == null)
			return;
		Gem.GemType currentGemType = Gem.GemType.none;
		int adjacency = 1;
		int highestAdjacency = 1;
		int endIdx = 0;
		for(int i=0 ; i < newGems.length; i++) {
			Gem gem = newGems[i];
			if(currentGemType.equals(gem.type) && 
					!gem.type.equals(Gem.GemType.none) && 
					!gem.type.equals(Gem.GemType.unknown)) {
				adjacency++;
				if(adjacency > highestAdjacency) {
					highestAdjacency = adjacency;
					endIdx = i;
				}
			} else {
				currentGemType = gem.type;
				adjacency = 1;
			}
		}
		if(highestAdjacency >= 3) {
			move.points += highestAdjacency;
			Gem firstGem = newGems[(endIdx-highestAdjacency) + 1];
			Gem lastGem = newGems[endIdx];
			boolean horiz = false;
			if(firstGem.y == lastGem.y) {
				horiz = true;
			}
			move.AddEffectArea(firstGem.x, firstGem.y, lastGem.x, lastGem.y, horiz);
		}
		return;
	}
	
}
